<?php
/**
 * 数据库操作类 （继承PDO）
 *
 * @author 陈小飞
 * @version  2007-08-16
 * @copyright 陈小飞
 */
 
class DB extends PDO {	
	 
	/**
	 * 调试开关 0=关闭 | 1=直接输出 | 2=保存不输出
	 * @var integer $debug
	 */	
	public $debug=0;
	 
	/**
	 * 调试信息
	 * @var string $debugMsg
	 */	
    public $debugMsg = '';	
	
	/**
	 * 错误输出 0=关闭 | 1=输出 | 3=保存在日志文件中 
	 * @var integer $errorOutput
	 */	
	public $errorOutput=1;

	public $errors = array();
	// 控制是否可以结束事务(commit或rollback)
	private $canEndTransaction = true;
	public $isBeginTransaction = false;//事务是否已开始
	   
	/**
	 * 构造函数
	 * @var array $dbConf 数据库配置
	 */
	function __construct($dbConf) {	
		
		$dbURL = "{$dbConf['provider']}:host={$dbConf['host']};dbname={$dbConf['dbName']}";
		try {
			parent::__construct($dbURL, $dbConf['userName'], $dbConf['passWord']);
			$this->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); //设置属性 强制列名小写			
		    $this->query('SET NAMES '.$dbConf['charset']);				  //设置数据库编码		
		
		} catch (PDOException $e) {
			//echo 'ERROR:DB001 ';
			die("Error: " . $e->__toString() . "<br/>");
			exit;
		}
	}
	
	
	/**
	 * 设置事务是否可提交状态
	 * @param boolean $state
	 */
	public function setTransactionState($state)
	{
		$this->canEndTransaction = $state;
	}
	
	/**
	 * (non-PHPdoc)
	 * @see PDO::beginTransaction()
	 */
	public function beginTransaction()
	{
		if ($this->isBeginTransaction) {
			return true;
		} else {
			parent::beginTransaction();
			$this->isBeginTransaction = true;
			// $canEndTransaction不可在这里设置true,必须让业务层,处理这个属性
			return true;
		}
	}
	
	/**
	 * (non-PHPdoc)
	 * @see PDO::commit()
	 */
	public function commit()
	{
		if ($this->isBeginTransaction) {
			if ($this->canEndTransaction) {
				parent::commit();
				$this->isBeginTransaction = false;
			}
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * (non-PHPdoc)
	 * @see PDO::commit()
	 */
	public function rollBack()
	{
		if ($this->isBeginTransaction) {
			if ($this->canEndTransaction) {
				parent::rollBack();
				$this->isBeginTransaction = false;
			}
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 根据SQL语句从数据表中取数据，只取第一条记录的值，
	 * 如果记录中只有一个字段，则只返回字段值。
	 * @param string $sql sql语句
	 * @param array $paramAry sql绑定参数（存在则使用参数化查询）
	 * @return array  返回查询的结果
	 * @access public
	 */
	public function getValue($sql,$paramAry=NULL) {
			
		$sql .= ' LIMIT 0,1' ;
		if($paramAry){		
			//参数化查询
			$result = $this->query_p($sql,$paramAry,'one');	
		} else {
			//常规查询		
			$result =  $this->query_m($sql,'one');	
		}
	
		if( $result ){
			$result = ( count($result)<=1 ) ? current($result) : $result;
		}

		return $result;
	}//end getValue
		
	/**
	 * 取多条记录
	 * @param string $sql sql语句
	 * @param array $paramAry sql绑定参数（存在则使用参数化查询）
	 * @param integer $form 开始行数
	 * @param integer $to 结束行数	 
	 * @return array 返回查询的结果
	 * @access public
	 */
	public function getList($sql,$paramAry=NULL,$form=0,$to=0,$singleField=false) {			
		
		if($form){
			$form--;
		} else {
			$form = 0;
		}
		
		if ($to) {		
			$to  -= $form;
			$sql .=' LIMIT '. $form. ','. $to;
		}		
		
		if($paramAry){		
			//参数化查询
			$ret = $this->query_p($sql,$paramAry,'all');	
		} else {
			//常规查询		
			$ret = $this->query_m($sql,'all');	
		}
		if ($singleField) {
			foreach ($ret as $k=>$v) {
				$ret[$k] = reset($v);
			}
		}
		
		if (!$ret && !is_array($ret)) {
			$ret = array();
		}
		
		return $ret;
	}//end getList
				
	/**
	 * 更新记录
	 * @param string $tableName 表名
	 * @param array $fieldArray 字段数组
	 * @param string $where 条件 	 	 
	 * @return integer  返回更新是否成功
	 * @access public
	 */
	public function update($tableName,$paramAry,$where=NULL) {
			
		$fieldAry = array();
		foreach($paramAry as $key=>$value){
			$fieldAry[] = "{$key}=:{$key}";
		}	
		if($where){
			$where = ' WHERE '.$where;
		}		
		$fieldStr 	= implode(',',$fieldAry);
		$sql 		= "UPDATE {$tableName} SET {$fieldStr} {$where}";

		$exe 		= $this->query_p($sql,$paramAry);
		
		if ($exe) {
			$row = $exe->rowCount();
			return $row===false ? false : true;
		} else {
			return false;
		}
	}//end update

	
	/**
	 * 插入一条记录
	 * @param string $tableName 表名
	 * @param array $paramAry sql绑定参数数组 	 
	 * @return integer 返回添加的主键(id)值
	 * @access public
	 */	
	public function insert($tableName,$paramAry) {				
				
		$fieldAry = array();
		$valueList = array();
		foreach($paramAry as $key=>$value){
			$fieldAry[] = '`'.$key.'`';				
			$fieldParm[] = ':'.$key;								
			$valueList[] = $value;				
		}		
		
		$fieldStr 	  = implode(',',$fieldAry);
		$fieldParmStr = implode(',',$fieldParm);
		$sql = "INSERT INTO `{$tableName}` ({$fieldStr})VALUES({$fieldParmStr})";
		
		$exe 		  = $this->query_p($sql,$paramAry);
		
		return PDO::lastInsertId();	
	}//end insert

		
	/**
	 * 删除记录
	 * @param string $tableName 表名 
	 * @param string $where 条件
	 * @param array $paramAry sql绑定参数数组 （存在则转为参数化sql） 
	 * @return bool $result 删除成功返回true,否则返回false
	 * @access public
	 */
	public function delete($tableName,$where,$paramAry=NULL){ 					
		
		$where = ' WHERE '.$where;		
		$sql = 'DELETE FROM '. $tableName.$where;	
		
		if( $paramAry ){
			//参数化sql
			$exe 	= $this->query_p($sql,$paramAry);
			$result = $exe->rowCount();
		} else {
			//常规sql
			$result = $this->exec($sql);
		}
		$this->checkDebug($sql);
		return $result;
	}//end delete
			
	/**
	 * 取记录数
	 * @param string $tableName 表名 
	 * @param string $where 条件
	 * @param array $paramAry sql绑定参数数组 （存在则转为参数化sql） 	 
	 * @return integer 返回记录数
	 * @access public
	 */
	public function getCount($tableName,$where=NULL,$paramAry=NULL){ 			
		
		$sql = 'SELECT COUNT(*) AS recordcount FROM '. $tableName;
		if($where){
			$sql .= ' WHERE '.$where;
		}
			
		return (int)$this->getValue($sql,$paramAry);			
	}//end getCount
	
	/**
	 * 执行常规sql查询(带返回值)
	 * @param string $sql sql语句
	 * @return array $result 返回查询的结果
	 * @access public
	 */
	private function query_m($sql,$returnRow='one'){
	
		$rs = $this->query($sql);
		
		if($rs===false){
			//SQL执行错误捕捉	
			$this->sqlError($this->errorInfo(),$sql);
			return false;
		}
		
		$rs->setFetchMode(PDO::FETCH_ASSOC);
		
		if($returnRow=='all'){
			$result = $rs->fetchAll();
		} else {
			$result = $rs->fetch();
		}
		
		$this->checkDebug($sql);
		return $result;
	}//end query_m
		
	/**
	 * 执行参数化sql查询
	 * @param string $sql sql语句
	 * @param array $paramAry sql参数化绑定参数
	 * @param string $returnType 返回数据类型（默认=不返回 'one'=返回一条记录 'all'=返回所有记录）
	 * @return array $result 返回查询的结果
	 * @access public
	 */
	private function query_p($sql,$paramAry,$returnType=NULL){
	
		$stmt = $this->prepare($sql);
		//绑定参数
		foreach($paramAry as $key=>$value){
						
			if( gettype($value)=='integer' ){
				$dataType = PDO::PARAM_INT;
			} else {
				$dataType = PDO::PARAM_STR;
			}
			$stmt->bindParam(':'.$key, $paramAry[$key]);		
		}
		$stmt->setFetchMode(PDO::FETCH_ASSOC);
		
		
		if( !$ret=$stmt->execute() ) {
			//SQL执行错误捕捉	
			$this->sqlError($stmt->errorInfo(),$sql,$paramAry);
			return false;
		}
		switch($returnType){		
		case 'one':
			$result = $stmt->fetch();
			break;
		case 'all':
			$result = $stmt->fetchAll();
			break;	
		default:
			$result = $stmt;
			break;
		}	
		$this->checkDebug($sql,$paramAry);

	
		return $result;	
	}//end query_p
		
	/**
	 * 执行sql(不带返回值)
	 * @param string $sql sql语句
	 * @param array $paramAry sql绑定参数数组 （存在则转为参数化sql） 
	 * @return bool $result 操作成功返回true,否则返回false
	 * @access public
	 */
	public function exeSql($sql,$paramAry=NULL){ 					
		
		if( $paramAry ){
			//参数化sql
			$result = $this->query_p($sql,$paramAry);		
		} else {
			//常规sql
			$result = $this->exec($sql);
			if( $result===false ){
				//SQL执行错误捕捉	
				$this->sqlError($this->errorInfo(),$sql);
			}
		}
			
		return $result;
	}//end exeSql
	
	/**
	 * debug 操作
	 * @param string $sql 条件	
	 * @param array $paramAry sql绑定参数数组 （存在表示当前为参数化sql） 	 	  
	 * @return void
	 * @access public
	 */
	public function checkDebug($msg,$paramAry=NULL) {		
	
		if(!$this->debug){
			return false;
		}
		
		if($paramAry){
			$ary = array();
			foreach($paramAry as $key=>$value){				
				$ary[] = "{$key}:$value";								
			}	
			$msg .= '<br>PARAM:'.implode(',',$ary).'<br>';
			
		}
		$msg .='<br><br>';
	    if($this->debug==1){
			//echo $msg;
		}else if($this->debug==2){
			$this->debugMsg .= $msg;
		}
	}//end checkDebug
	
	/**
	 * 获取数据库中所有的表
	 * @return array()
	 */
	public function showTables()
	{
		$sql = 'show tables';
		$arr = self::getList($sql);
		$result = array();
		foreach ($arr as $v) {
			$result[] = reset($v);
		}
		return $result;
	}
	
	/**
	 * 获取表字段详细说明
	 * @param string $tbl
	 */
	public function desc($tbl)
	{
		$sql = 'desc '.$tbl;
		return self::getList($sql);
	}
	
	/**
	 * 执行sql错误输出
	 * @param string $errorMsg 错误信息
	 * @param string $sql 执行的sql
	 * @param array $paramAry sql绑定参数 数组
	 * @return void
	 * @access public
	 */
	private function sqlError($errorInfo,$sql='',$paramAry=NULL){
		switch($this->errorOutput){
		case 1://直接输出
			break;
		case 2://保存到日志文件中
			break;
		default:
			die(L('system_error'));
			break;
		}
		$errorMsg = "SQL：{$sql}<br>ErrorCode：{$errorInfo[0]} ({$errorInfo[1]})<br>Error Describe ：{$errorInfo[2]}";			

		$this->errors = array(
			'code' => $errorInfo[0].'('.$errorInfo[1].')',
			'msg'  => $errorInfo[2],
			'sql'  => $sql,
			'param'=> var_export($paramAry, true),
		);

//		echo $errorMsg;
//		exit;
//		Log::write($errorMsg,UID,125);
		
//		exit;		
		
//		if($paramAry){
//			
//			die('001 错误, 请马上联系管理员！');
//			echo 'param:<pre>';
//			var_dump($paramAry);
//		}
//		die($errorMsg);
	}//end error
}
?>